import bisect

import qasync
from PySide6.QtCore import QObject, Signal, Property, Slot, QTimer
from PySide6.QtQml import QmlElement

from Lib.vlc_media_player import VlcMediaPlayer
from src.common.signal_bus import SignalBus
from src.library.load_play_url import load_play_url_by_music_meta
from src.library.lyric import get_kuwo_lrclist_by_mid
from src.library.model import MusicMeta
from src.utils.debug_tools import print_debug

QML_IMPORT_NAME = "Backend"
QML_IMPORT_MAJOR_VERSION = 1


@QmlElement
class PlayerViewModel(QObject):
    volumeChanged = Signal()
    playPositionChanged = Signal()
    isPlayingChanged = Signal()
    hasMusicChanged = Signal()
    musicInfoChanged = Signal()
    lyricsChanged = Signal()
    currentLyricLineChanged = Signal()
    currentLyricIndexChanged = Signal()
    currentPlaylistTotalChanged = Signal()

    def __init__(self):
        super().__init__()
        self.__retry_count = 0
        self.__first_play = True
        self._current_playlist_total = 0
        self._volume = 50
        self._current_lyric_line = "永远相信美好的事情即将发生。。。"
        self._current_lyric_index = -1
        self._play_postion = 0.0
        self._isPlaying = False
        self._signalBus = SignalBus()
        self._hasMusic = False
        # 当前播放的音乐
        self._current_music: MusicMeta = None
        self._init_player()
        self._bind_bus()
        self.isPlayingChanged.connect(
            lambda: self._player.play() if self._isPlaying else self._player.pause()
        )

    ########################################################
    ## 与QML绑定的属性
    ########################################################
    @Property(list, notify=lyricsChanged)
    def lyrics(self):
        lyrics_list = self._current_music.lyric.values() if self._current_music and self._current_music.lyric else []
        return list(lyrics_list)

    @Property(int, notify=currentPlaylistTotalChanged)
    def currentPlaylistTotal(self):
        return self._current_playlist_total

    @Property(int, notify=volumeChanged)
    def volume(self):
        return self._volume

    @volume.setter
    def volume(self, value):
        if self._volume == value:
            return
        self._volume = value
        self.volumeChanged.emit()

    @Property(str, notify=currentLyricLineChanged)
    def currentLyricLine(self):
        return self._current_lyric_line

    @Property(int, notify=currentLyricIndexChanged)
    def currentLyricIndex(self):
        # 返回当前播放时间对应的歌词索引，类型为int，用于高亮显示当前歌词
        return self._current_lyric_index

    @Property(str, notify=musicInfoChanged)
    def musicInfo(self):
        return f"{self._current_music.name} - {self._current_music.album if self._current_music.album else "未知专辑"}" if self._current_music else "Are You Ok? - 雷子"

    @Property(str, notify=musicInfoChanged)
    def albumCover(self):
        return self._current_music.album_pic if self._current_music else ""

    @Property(bool, notify=hasMusicChanged)
    def hasMusic(self):
        return self._hasMusic

    @Property(float, notify=playPositionChanged)
    def playPosition(self):
        return self._play_postion

    @Property(bool, notify=isPlayingChanged)
    def isPlaying(self):
        return self._isPlaying

    @isPlaying.setter
    def isPlaying(self, value):
        self._isPlaying = value
        self.isPlayingChanged.emit()

    ########################################################
    ## QML调用的方法
    ########################################################
    @Slot()
    def nextMusic(self):
        self._signalBus.nextMusic.emit()

    @Slot()
    def preMusic(self):
        self._signalBus.preMusic.emit()

    @Slot(int)
    def setVolume(self, volume):
        if not self._player:
            return
        self._player.setVolume(volume)
        self.volume = volume

    @Slot(bool)
    def setMute(self, mute):
        self._player.setMute(mute)

    @Slot(float)
    def setPlayPosition(self, pos):
        self._player.setPosition(pos)

    @Slot(int)
    def setPositionByLyricIndex(self, index):
        if not self._current_music:
            return
        lyric = self._current_music.lyric
        if not lyric:
            return
        times = list(lyric.keys())
        if index < 0 or index >= len(times):
            return
        self.isPlaying = True
        pos = times[index] / self._current_music.duration
        QTimer().singleShot(100, lambda: self._player.setPosition(pos))

    @Slot(float, result=str)
    def getLineLyric(self, pos):
        return self._get_lyric_line(pos * self._player.getDuration())[1]

    ########################################################
    ## 绑定总线信号与其他viewmodel进行通信
    ########################################################
    def _bind_bus(self):
        @Slot(int)
        def onPlaylistTotalChanged(total):
            self._current_playlist_total = total
            self.currentPlaylistTotalChanged.emit()

        self._signalBus.playlistTotalChanged.connect(onPlaylistTotalChanged)
        self._signalBus.playMusic.connect(self._setMusic2Player)
        self._signalBus.closeApp.connect(self._on_close_app)
        self._signalBus.resetPlayer.connect(self._reset_player)

    ########################################################
    ## 内部方法
    ########################################################
    def _reset_player(self):
        self._player.stop()
        self._current_music = None
        self.musicInfoChanged.emit()
        self._set_has_music(False)
        self._set_play_position(0.0)
        self._current_lyric_line = "永远相信美好的事情即将发生。。。"
        self.currentLyricLineChanged.emit()

    def _on_close_app(self):
        # Ensure playback is stopped before closing
        if self._player.isPlaying():
            self._player.stop()

        # self._player.deleteLater()

        # # Release VLC resources
        self._player.destroyPlayer()

    def _get_lyric_line(self, val):
        """
        获取当前播放时间对应的歌词
        :param val:  当前播放时间 单位秒
        :return: 返回当前播放时间对应的歌词索引和歌词内容
        """

        if not self._current_music:
            return -1, "暂无歌词"

        lyrics = self._current_music.lyric
        if not lyrics:
            return -1, "暂无歌词"
        # 从映射中查找不超过当前播放时间的最大歌词时间
        times = list(lyrics.keys())
        index = bisect.bisect_right(times, val)
        if index:
            closest_time = times[index - 1]
            return index - 1, lyrics[closest_time]
        return -1, "暂无歌词"

    def _set_current_lyric_line(self, line):
        if self._current_lyric_line == line:
            return
        self._current_lyric_line = line
        self.currentLyricLineChanged.emit()

    def _set_current_lyric_index(self, index):
        if self._current_lyric_index == index:
            return
        self._current_lyric_index = index
        self.currentLyricIndexChanged.emit()

    def _set_play_position(self, pos):
        if pos == self._play_postion:
            return
        self._play_postion = pos
        self.playPositionChanged.emit()
        index, line = self._get_lyric_line(self._player.getCurrentTime())
        self._set_current_lyric_line(line)
        self._set_current_lyric_index(index)

    def _set_has_music(self, value):
        if self._hasMusic == value:
            return
        self._hasMusic = value
        self.hasMusicChanged.emit()

    def _init_player(self):
        self._player = VlcMediaPlayer()
        # 连接播放器的信号
        self._player.mediaPositionChanged.connect(self._set_play_position)

        def onMediaStateChanged(state):
            if state == "Playing":
                self.__retry_count = 0
                self._current_lyric_line = "正在播放..."
                self.currentLyricLineChanged.emit()
            elif state == "Paused":
                self._current_lyric_line = "暂停播放..."
                self.currentLyricLineChanged.emit()
            elif state == "Ended":
                self.nextMusic()
            elif state == "Buffering":
                self._current_lyric_line = "缓冲中..."
                self.currentLyricLineChanged.emit()

        self._player.mediaStateChanged.connect(onMediaStateChanged)
        self._player.errorOccurred.connect(self._retry_load_play_url_to_music)

    async def _load_lyrics_to_music(self):
        music_mid = self._current_music.play_id
        if music_mid is None:
            return
        lyric = await get_kuwo_lrclist_by_mid(music_mid)
        self._current_music.lyric = lyric
        self.lyricsChanged.emit()

    @qasync.asyncSlot()
    async def _retry_load_play_url_to_music(self):
        self._player.stop()
        self.__retry_count += 1
        if self.__retry_count <= 1:
            self._current_lyric_line = f"播放失败，正在第{self.__retry_count}重试..."
            self.currentLyricLineChanged.emit()
            self._current_music.play_url = ""
            await self._load_play_url(self._current_music)
        else:
            self.__retry_count = 0
            self._current_lyric_line = "播放失败，已重试2次"
            self.currentLyricLineChanged.emit()
            self.isPlaying = False

    async def _load_play_url(self, music_meta: MusicMeta):
        # print_debug(f"load play url for {music_meta.name}")
        # print_debug(f"play url: bbb{music_meta.play_url}")
        if not music_meta.play_url:
            play_url = await load_play_url_by_music_meta(music_meta)
            if play_url:
                music_meta.play_url = play_url
        # print_debug(f"play url: {music_meta.play_url}")
        self._player.setMediaUrl(music_meta.play_url)
        if self.__first_play:
            self.__first_play = False
            return
        self.isPlaying = True

    @qasync.asyncSlot()
    async def _setMusic2Player(self, music_meta: MusicMeta):
        if self._current_music == music_meta:
            return
        self._set_play_position(0.0)
        self._current_music = music_meta
        self.musicInfoChanged.emit()
        self._set_has_music(True)
        self.isPlaying = False
        await self._load_play_url(music_meta)
        await self._load_lyrics_to_music()
